home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
System Booster
/
System Booster.iso
/
Archives
/
ForCLI
/
HexD2.lha
/
HexD.a
< prev
next >
Wrap
Text File
|
1993-02-21
|
32KB
|
1,396 lines
*
* HexD. A program that quickly dumps any file(s) in hex format on standard
* out or a file.
*
* Version history:
*
* Magnus Holmgren:
* V1.0 24-11-91 : First release (portable C-code).
*
* V2.0 21-02-93 : Not portable any more, requires AmigaDOS 2.0.
* : Downcoded to assembler. Many enhanchements.
*
* Warning: This source is a bit messy! Major cleanup could be done. But
* that would make the program larger! :) :)
*
OPT O+,D-
INCDIR INC:
INCLUDE exec/types.i
INCLUDE exec/funcdef.i
INCLUDE exec/alerts.i
INCLUDE exec/execbase.i
INCLUDE exec/exec_lib.i
INCLUDE exec/memory.i
INCLUDE dos/dosasl.i
INCLUDE dos/dosextens.i
INCLUDE dos/dos_lib.i
INCLUDE graphics/text.i
INCLUDE intuition/intuition.i
*
* Flags structure
*
STRUCTURE Flags,0
APTR f_Files ; The files to show
APTR f_To ; Output file
APTR f_Width ; Width of output
LONG f_All ; Enter subdirectories?
LONG f_Append ; Append to TO file?
LONG f_Hex ; Hex only output?
LONG f_Ascii ; Ascii only output?
LONG f_NoOffset ; Show offset?
LONG f_Header ; Show a header?
LONG f_Cursor ; Hide cursor?
LONG f_Page ; Show page prompt?
LABEL f_SIZEOF ; Size of flags struct
* To make it easier to keep both up-to-date
TEMPLATE MACRO
dc.b "FILES/M,TO/K,WIDTH/K/N,ALL/S,APPEND/S,HEX/S,ASCII/S,"
dc.b "NOOFFSET/S,HEADER/S,CURSOR/S,PAGE/S",0
ENDM
* Maximum length allowed for one filename
FILENAMESIZE EQU 512
* Maximum width to display on screen. Minimal width is "floating"...
MAXWIDTH EQU 512
* Default width (if out file is interactive, get default from there instead)
DEFWIDTH EQU 75
* Starting value for the dt_YPos variable
INITYPOS EQU 3
*
* Data structure. All data used by us. Gets allocated, for purity.
*
STRUCTURE DataTable,0
APTR dt_DOSBase
APTR dt_SysBase
APTR dt_InitialSP ; Stack pointer at start
APTR dt_InFile ; File we currently are reading
APTR dt_OutFile ; File we are writing data to
APTR dt_RdArgs ; RDArgs structure
STRUCT dt_Flags,f_SIZEOF ; Flags array
ALIGNLONG ; Better long-align AnchorPath (probably not needed here)
STRUCT dt_AP,ap_SIZEOF ; AnchorPath, for pattern matching
STRUCT dt_APNameBuffer,FILENAMESIZE ; Filename buffer (part of AnchorPath)
ALIGNLONG ; Long align again, for speed
STRUCT dt_ReadBuffer,MAXWIDTH+2 ; Buffer for reading data
ALIGNLONG ; Long align again, for speed
STRUCT dt_WriteBuffer,MAXWIDTH+2 ; Buffer while building output string
ALIGNWORD ; Better word-align
LONG dt_BytePos ; Byte position in file
UWORD dt_Width ; Number of chars/row
UWORD dt_Height ; Number of rows (for pager if > 0)
UWORD dt_YPos ; Current output line (for pager)
UWORD dt_MinWidth ; Minimal width allowed
UWORD dt_EndOfHex ; Position on line at which hex part ends
UWORD dt_Response ; Buffer for pager
UBYTE dt_DisplayFormat ; How to show the data
UBYTE dt_OutOpened ; We have opened Out (cleanup)
UBYTE dt_InOpened ; We have opened In (cleanup)
UBYTE dt_Matching ; An MatchFirst() have been used without,
; a corresponding MatchEnd() (cleanup).
LABEL dt_SIZEOF ; Size of data table
*
* Different values in dt_DisplayFormat byte:
*
BOTH EQU 0 ; Both Ascii and Hex
ASCII EQU 1 ; Only Ascii
HEX EQU 2 ; Only Hex
*
* Macro to generate the "minimal width array". Take the dt_DisplayFormat
* byte, add NOOFFSET (currently 4) if f_NoOffset is TRUE, and use that value
* (shifted properly) to index in this word-array, to get the *REAL* minimal
* width (in chars) allowed.
*
NOOFFSET EQU 4
MINWIDTHARRAY MACRO
dc.w 14,11,10,0,6,3,2,0
ENDM
* A few handy macros
* Call a library routine
SYS MACRO
jsr _LVO\1(a6)
ENDM
* Call a subroutine
CALL MACRO
bsr \1
ENDM
* Quickly clear a data register
CLD MACRO
moveq #0,d\1
ENDM
* Quickly clear an address register
CLA MACRO
sub.l a\1,a\1
ENDM
*
* Push register list to stack. Remembers the last list used. If 'NOREM'
* is the second argument, the pushed list will not be remembered.
*
PUSHM MACRO
movem.l \1,-(sp)
IFNC '\2','NOREM'
PUSHM_COUNT SET PUSHM_COUNT+1
PUSHM_\<PUSHM_COUNT> REG \1
ENDC
ENDM
*
* Restore register list from stack. If no argument, restore last register
* list remembered.
*
POPM MACRO
IFC '\1',''
movem.l (sp)+,PUSHM_\<PUSHM_COUNT>
ELSEIF
movem.l (sp)+,\1
ENDC
ENDM
*
* Works like PUSHM, but works on single registers only (needed since GenIm2
* won't optimize "single-register movem" :( ).
*
PUSH MACRO
move.l \1,-(sp)
IFNC '\2','NOREM'
PUSH_COUNT SET PUSH_COUNT+1
PUSH_\<PUSH_COUNT> EQUR \1
ENDC
ENDM
*
* Like POPM, but a signle register.
*
POP MACRO
IFC '\1',''
move.l (sp)+,PUSH_\<PUSH_COUNT>
ELSEIF
move.l (sp)+,\1
ENDC
ENDM
* Init some counters for the PUSH(M)/POP(M) macros
PUSHM_COUNT SET 0
PUSH_COUNT SET 0
SECTION Code,CODE
*
* Init data area, open needed libraries.
*
Init move.l 4.w,a6 ; First get SysBase
CLA 1
SYS FindTask
move.l d0,a5 ; Our process struct
lea DOSName_Msg(pc),a1 ; dos.library
CLD 0 ; Any version
SYS OpenLibrary ; Try to open it
move.l d0,a3 ; Save DOSBase
tst.l d0 ; Did we get it?
bne.b .dos_ok ; Yep
move.l #AT_Recovery|AG_OpenLib|AO_DOSLib,d7 ; Alert! No DOS!
SYS Alert ; Show alert
move.l #RETURN_FAIL,d0 ; Return failure
rts ; And end
.dos_ok:
cmp.w #36,LIB_VERSION(a6) ; Do we have OS 2.04+?
bgt .os_ok ; Yep
move.l a3,a6 ; Get DOSBase
SYS Output ; Get output handle
move.l d0,d1
beq.b .no_out ; No output
lea NeedOS2_Msg(pc),a0 ; We need OS 2.04+
move.l a0,d2
move.l #NeedOS2_SIZEOF,d3 ; Size of message
SYS Write ; Inform user about problem
.no_out:
moveq #ERROR_INVALID_RESIDENT_LIBRARY,d0
move.l d0,pr_Result2(a5) ; Set secondary result
move.l #RETURN_FAIL,d0 ; Return failure
rts ; And end
.os_ok:
move.l #dt_SIZEOF,d0 ; Size of data struct
move.l #MEMF_CLEAR,d1 ; Clear the memory
SYS AllocVec ; Try to allocate DataTable
tst.l d0 ; Got it?
bne.b .mem_ok ; Yep
move.l a3,a6 ; DOSBase
move.l #ERROR_NO_FREE_STORE,d1 ; Ran out of memory
lea PrgName_Msg(pc),a0
move.l a0,d2 ; Header for error message
SYS PrintFault ; Show error message
move.l #ERROR_NO_FREE_STORE,d1 ; Ran out of memory
SYS SetIoErr ; Set return code
move.l #RETURN_FAIL,d0 ; Return failure
rts ; End
.mem_ok:
move.l d0,a4 ; Get data structure
move.l a3,dt_DOSBase(a4) ; Init some data
move.l a6,dt_SysBase(a4)
move.l sp,dt_InitialSP(a4)
CALL Main ; Call main part
move.l #RETURN_OK,d0 ; Ok return
CLD 1 ; Fall through to CleanExit
*
* CleanExit( __D0 ULONG code, __D1 ULONG res2 );
*
* Clean up everything and exit gracefully. IoErr() will be set to res2.
* code is returned to the user. This function never returns.
*
CleanExit
move.l dt_DOSBase(a4),a6 ; Get DOSBase
move.l dt_InitialSP(a4),sp ; Restore original SP
move.l d0,d6 ; Save error codes
move.l d1,d7
lea CursorOn_Msg(pc),a0
move.l a0,d1
SYS PutStr ; Restore cursor, if hidden
move.l dt_RdArgs(a4),d1 ; Any RDArgs structure?
beq.b .no_rdargs ; No
SYS FreeArgs ; Free RDArgs structure
.no_rdargs:
tst.b dt_InOpened(a4) ; Have we opened the in file?
beq.b .no_in ; No
move.l dt_InFile(a4),d1 ; Any input file left open?
beq.b .no_in ; No
SYS Close ; Close input file
.no_in:
tst.b dt_OutOpened(a4) ; Have we opened the out file?
beq.b .no_out ; No
move.l dt_OutFile(a4),d1 ; Any output file left open?
beq.b .no_out ; No
SYS Close ; Close output file
.no_out:
tst.b dt_Matching(a4) ; Any unbalanced MatchFirst()?
beq.b .no_match ; Nope
lea dt_AP(a4),a0
move.l a0,d1 ; AnchorPath
SYS MatchEnd ; Clean up matching stuff
.no_match:
move.l a6,a3 ; Save DOSBase
move.l dt_SysBase(a4),a6
move.l a4,a1 ; Get DataTable
SYS FreeVec ; Free it
exg a3,a6 ; Get DOSBase again
move.l d7,d1 ; Get secondary result
SYS SetIoErr ; Set result2
move.l a6,a1 ; DOSBase
move.l a3,a6 ; SysBase
SYS CloseLibrary ; Close dos.library
move.l d6,d0 ; Get return code
rts ; And exit
*
* VOID PrintFault( __A0 header, __D0 ULONG result2 );
*
* Print reason for failure and exit.
*
PrintFault
move.l d0,d1
move.l d1,d3 ; Save result2
move.l a0,d2 ; Error header
SYS PrintFault ; Print message
move.l #RETURN_FAIL,d0 ; Return code
move.l d3,d1 ; And reason for it
CALL CleanExit ; Exit
*
* Main part. All "basic" data have been properly set up
*
Main move.l dt_DOSBase(a4),a6 ; We use DOS a lot
** Set some default values **
move.w #FILENAMESIZE,dt_AP+ap_Strlen(a4)
move.l #SIGBREAKF_CTRL_C,dt_AP+ap_BreakBits(a4)
move.w #DEFWIDTH,dt_Width(a4)
move.w #INITYPOS,dt_YPos(a4)
** Parse arguments **
lea Template_Msg(pc),a0
move.l a0,d1 ; Template
lea dt_Flags(a4),a0
move.l a0,d2 ; Flags array
CLD 3 ; No RDArgs structure
SYS ReadArgs ; Parse arguments
move.l d0,dt_RdArgs(a4) ; Save RDArgs struct
bne.b .args_ok ; No errors
SYS IoErr ; Why failure?
lea PrgName_Msg(pc),a0
CALL PrintFault
.args_ok:
tst.l dt_Flags+f_Hex(a4) ; Only hex part?
beq.b .no_hex ; No
move.b #HEX,dt_DisplayFormat(a4) ; Only show hex part
.no_hex:
tst.l dt_Flags+f_Ascii(a4) ; Only Ascii part?
beq.b .no_ascii ; No
move.b #ASCII,d0 ; Default is Ascii mode
cmp.b #HEX,dt_DisplayFormat(a4) ; Hex mode already requested?
bne.b .no_ascii_hex ; Nope
move.b #BOTH,d0 ; Else use Both mode
.no_ascii_hex:
move.b d0,dt_DisplayFormat(a4) ; Set display mode
.no_ascii:
move.l dt_Flags+f_Width(a4),d0 ; Any width pointer?
beq.b .no_width ; No
move.l d0,a0 ; Get pointer
move.l (a0),d1 ; And the value
move.w d1,dt_Width(a4) ; Save it
.no_width:
lea MinWidthArray(pc),a0 ; Get array for finding proper value
CLD 0
move.b dt_DisplayFormat(a4),d0 ; Get display format
tst.l dt_Flags+f_NoOffset(a4) ; Any offset?
beq.b .offset ; Yep
add.w #NOOFFSET,d0 ; Add no offset value
.offset:
add.w d0,d0 ; Make value index words
move.w (a0,d0.w),d0 ; Get minimal width
move.w d0,dt_MinWidth(a4) ; Save value
move.w dt_Width(a4),d1 ; To small?
cmp.w d0,d1
bge.b .min_ok ; No
.size_err:
lea WidthError_Msg(pc),a0 ; Format string
CALL Printf1 ; Show error message
move.l #RETURN_FAIL,d0 ; Return failure
CLD 1 ; No suitable ERROR_* code
CALL CleanExit ; Exit
.min_ok:
cmp.w #MAXWIDTH,d1 ; To large?
bgt.b .size_err ; Yes
** Open TO file, if any **
move.l dt_Flags+f_To(a4),d1 ; Any TO file?
beq .no_to ; Nope
move.l #MODE_NEWFILE,d2 ; Default is create the file
tst.l dt_Flags+f_Append(a4) ; Append to file?
beq.b .del_old ; No
move.l #MODE_READWRITE,d2 ; Don't erase any old file
.del_old:
SYS Open ; Try to open it
move.l d0,dt_OutFile(a4) ; Save pointer
bne.b .to_ok ; TO file opened ok
SYS IoErr ; Get error code
move.l d0,d3 ; Save it
move.l dt_Flags+f_To(a4),d0 ; Get TO file
lea NoToFile_Msg(pc),a0 ; Format string
CALL Printf1 ; Print error message
move.l d3,d0 ; IoErr() error code
CLA 0 ; Header already printed
CALL PrintFault
.to_ok:
st dt_OutOpened(a4) ; Flag that we have opened the out file
tst.l dt_Flags+f_Append(a4) ; Append to file?
beq.b .out_ok ; No
move.l #0,d1
SYS SetIoErr ; Clear error code
move.l dt_OutFile(a4),d1 ; File
move.l #0,d2 ; Offset
move.l #OFFSET_END,d3 ; Seek to end of file
SYS Seek
move.l d0,d3 ; Save current pos
SYS IoErr ; Get error code
moveq #-1,d1
cmp.l d1,d3 ; Seek ok?
beq.b .seek_error ; No
tst.l d0 ; Any error?
beq.b .out_ok ; No
.seek_error:
move.l d0,d3
move.l dt_Flags+f_To(a4),d0 ; Get TO file
lea NoAppend_Msg(pc),a0 ; Format string
CALL Printf1 ; Print error message
move.l d3,d0 ; IoErr() error code
CLA 0 ; Header already printed
CALL PrintFault
.no_to:
SYS Output ; Get standard output
move.l d0,dt_OutFile(a4) ; Save pointer
bne.b .out_ok
move.l #RETURN_FAIL,d0 ; Return failure
CLD 1 ; Dunno why this should happen.. :)
CALL CleanExit ; Exit
.out_ok:
move.l dt_OutFile(a4),d1 ; Output file
SYS IsInteractive ; Interactive file?
tst.l d0
bne.b .match ; Yep
clr.l dt_Flags+f_Page(a4) ; Disable pager
st dt_Flags+f_Cursor(a4) ; Make it != NULL
** Do all pattern matching **
.match:
move.l dt_Flags+f_Files(a4),a3 ; Get files array
lea dt_AP(a4),a2 ; Get AnchorPath struct
** Walk through all FILE arguments **
.next_file:
tst.l (a3) ; End of filename array?
beq .done ; Yep
** And pattern match this argument **
move.l (a3)+,d1 ; Get name/pattern
move.l a2,d2 ; AnchorPath
SYS MatchFirst ; Start matching this name
st dt_Matching(a4) ; We have called MatchFirst()
tst.l d0
bne.b .error ; Error!
.next_match:
tst.l ap_Info+fib_DirEntryType(a2) ; Type of entry?
beq.b .matchnext ; Not file, nor dir (??)
ble.b .file ; A file
bclr #APB_DIDDIR,ap_Flags(a2) ; Just did a dir?
bne.b .matchnext ; Yep, continue processing
tst.l dt_Flags+f_All(a4) ; Enter directories?
beq.b .matchnext ; Nope
bset #APB_DODIR,ap_Flags(a2) ; Else enter dir
bra.b .matchnext ; And continue
.file: CALL DumpFile ; Handle that file
.matchnext:
move.l a2,d1 ; AnchorPath
SYS MatchNext ; Do next file
tst.l d0 ; Everything ok?
beq.b .next_match ; Yep
.error:
move.l d0,d7 ; Save error code
move.l a2,d1 ; AnchorPath
SYS MatchEnd ; End matching
sf dt_Matching(a4) ; MatchFirst() "syncronised"
cmp.l #ERROR_NO_MORE_ENTRIES,d7 ; Done with this round?
beq .next_file ; Yep, do next file
cmp.l #ERROR_BREAK,d7 ; Break'ed?
bne.b .no_break ; No
CALL PrintBreak ; Print break message
move.l #RETURN_OK,d0 ; Return Ok
CLD 1
CALL CleanExit ; Exit
.no_break:
move.l d7,d0 ; Failure reason
lea PrgName_Msg(pc),a0
CALL PrintFault
.done: move.l dt_Flags+f_Files(a4),a0 ; Get filenames array
tst.l (a0) ; Any files specified at all?
bne.b .rts ; Yep
SYS Input ; Get standard input
move.l d0,dt_InFile(a4) ; Save handle
beq.b .rts ; No handle
move.l d0,d1
SYS Flush ; To be on the safe side (we are going to read)
CALL HexDump ; Dump the file
.rts: rts
*
* DumpFile();
*
* Simply dump the file in the AnchorPath to dt_OutFile, using suitable
* parameters (as found in the DataTable ). Prints a header if that was
* requested. May exit if fatal error, breaked or similar.
*
DumpFile
PUSHM d0-3/a0-2/a6
move.l dt_DOSBase(a4),a6 ; We only use DOS calls here
lea dt_AP+ap_Buf(a4),a2 ; Pathname buffer
move.l a2,d1 ; File name
move.l #MODE_OLDFILE,d2 ; Old file
SYS Open ; Try to open it
move.l d0,dt_InFile(a4) ; Save handle
bne.b .open_ok ; File opened ok
SYS IoErr ; Why didn't file open?
move.l d0,d3 ; Save reason
move.l a2,d0 ; Argument
lea NoInFile_Msg(pc),a0 ; Format string
CALL Printf1 ; Print error message
move.l d3,d0 ; Why file didn't open
CLA 0 ; No header
CALL PrintFault ; Print "reason"
.open_ok:
st dt_InOpened(a4) ; We have opened the in file
tst.l dt_Flags+f_Header(a4) ; Show a header?
beq.b .write_ok ; No
move.l dt_OutFile(a4),d1 ; Output file
lea Header_Msg(pc),a0
move.l a0,d2 ; Format string
PUSH a2 ; Argument
move.l sp,d3 ; Pointer to argument array
SYS VFPrintf ; Print header
add.l #4,sp ; Restore stack
tst.l d0 ; Write ok?
bgt.b .write_ok ; Yes
.write_error:
SYS IoErr ; Get error message
lea WriteError_Msg(pc),a0 ; Couldn't write
CALL PrintFault
.write_ok:
CALL HexDump ; Do it
move.l dt_InFile(a4),d1 ; Input file
SYS Close ; Close it (can hardly fail, since we read the file... ;)
clr.l dt_InFile(a4) ; Clear pointer
sf dt_InOpened(a4) ; No infile opened by us
POPM
rts
*
* VOID HexDump( VOID );
*
* Dump dt_InFile on dt_OutFile. Gets its paramteres from the DataTable.
* Checks height, width, paging (if so, when) etc.
*
* Register usage:
* D7 - Bytes per row
* D5 - Number of bytes in last read operation
* A3 - Pointer in work buffer
*
HexDump
PUSHM d0-7/a0-3/a6
move.l dt_DOSBase(a4),a6 ; We only use DOS calls
CALL CalcBytesPerRow ; Get # of bytes that fits on one row
move.l d0,d7 ; Save value
move.l d7,d5 ; Need to init # of bytes read
clr.l dt_BytePos(a4) ; Start at byte 0 :)
tst.l dt_Flags+f_Cursor(a4) ; Hide cursor?
bne.b .next_row ; No
move.l dt_OutFile(a4),d1 ; Output file
lea CursorOff_Msg(pc),a0
move.l a0,d2
SYS FPuts ; Hide cursor
tst.l d0
beq.b .next_row ; No write error
.write_error:
SYS IoErr
lea WriteError_Msg(pc),a0
CALL PrintFault
.next_row:
CALL CheckBreak ; Break pressed?
tst.l d0
bne .done ; Yep, CTRL-D pressed
cmp.l d7,d5 ; Did last read fill whole buffer?
blt .done ; No, must be end of file
** Get data for next output line **
CLD 1
SYS SetIoErr ; Clear IoErr() code
move.l dt_InFile(a4),d1 ; Input file
lea dt_ReadBuffer(a4),a0
move.l a0,d2 ; Read buffer
move.l #1,d3 ; Element size
move.l d7,d4 ; Number of elements
SYS FRead ; Read data
move.l d0,d5 ; Save amount read
SYS IoErr ; Get error code
tst.l d0 ; Save it
ble.b .read_ok ; No error
.read_error:
lea ReadError_Msg(pc),a0
CALL PrintFault
.read_ok:
tst.l d5 ; End of input?
beq .done ; Yep
** Build output string **
lea dt_WriteBuffer(a4),a3 ; Start of write buffer
lea HexTable(pc),a0 ; Hex conversion table
move.l #$0f,d1 ; Mask for hex conversion
move.l #4,d2 ; Steps to shift for each char
tst.l dt_Flags+f_NoOffset(a4) ; Display offset?
bne.b .no_offset ; No
move.l dt_BytePos(a4),d0 ; Get current byte position
move.l #5,d3 ; Count
add.l #6,a3 ; Start char + 1
.make_offset:
move.l d0,d4 ; Get value
lsr.l d2,d0 ; Shift to next char
and.l d1,d4 ; Get lower bits
move.b 0(a0,d4.w),-(a3) ; Get hex char
dbra d3,.make_offset ; Loop back
add.l #6,a3 ; Go to end of converted text
move.b #':',(a3)+
move.b #' ',(a3)+
.no_offset:
cmp.b #ASCII,dt_DisplayFormat(a4) ; Only Ascii?
beq.b .ascii ; Yes
lea dt_ReadBuffer(a4),a1 ; Read buffer
; lea HexTable(pc),a0 ; Hex conversion table
; move.l #$0f,d1 ; Mask for hex conversion
; move.l #4,d2 ; Steps to shift for each char
move.l #1,d3 ; Count
move.l #3,d4 ; Space mask
CLD 0 ; Better clear work registers
CLD 6
.make_hex:
move.b (a1)+,d6 ; Get char
move.l d6,d0
lsr.l d2,d0 ; Get high nybble
move.b (a0,d0.w),(a3)+ ; Get hex char
and.l d1,d6 ; Get low nybble
move.b (a0,d6.w),(a3)+ ; Get hex char
move.l d3,d0 ; Get offset in line
and.l d4,d0 ; Mask out lower 2 bits
bne.b .next_make_hex ; No time for space
move.b #' ',(a3)+ ; Write a space
.next_make_hex:
add.l #1,d3 ; Increase counter
cmp.l d3,d5 ; All bytes on this row done?
bge.b .make_hex ; No
clr.b (a3) ; NULL terminate string so far
cmp.b #BOTH,dt_DisplayFormat(a4) ; Both Ascii and Hex?
bne.b .row_done ; Nope, this row is finished
lea dt_WriteBuffer(a4),a0
add.w dt_EndOfHex(a4),a0 ; Address of end of Hex-part
move.l a0,d0
sub.l a3,d0
bra.b .start_pad
.pad_space:
move.b #' ',(a3)+ ; Pad out with spaces
.start_pad:
dbra d0,.pad_space
.ascii:
lea dt_ReadBuffer(a4),a0 ; Read data
lea AsciiTable(pc),a1 ; Conversion table
move.l d5,d0 ; Bytes/line
sub.l #1,d0 ; Prepare for dbra
CLD 1
move.b #"'",(a3)+ ; Start of ascii part
.next_ascii:
move.b (a0)+,d1 ; Get next byte
move.b (a1,d1.w),(a3)+ ; Show proper char
dbra d0,.next_ascii
move.b #"'",(a3)+ ; End of ascii part
.row_done:
move.b #10,(a3)+ ; Place a linefeed at the end
clr.b (a3) ; And terminate the string
add.l d5,dt_BytePos(a4) ; Increment byte position counter
move.l dt_OutFile(a4),d1 ; Output file
lea dt_WriteBuffer(a4),a0
move.l a0,d2 ; Write buffer
SYS FPuts ; Write out string
tst.l d0 ; Write ok?
bmi .write_error ; No
tst.l dt_Flags+f_Page(a4) ; Page output?
beq .next_row ; No
tst.w dt_Height(a4) ; Any height?
beq .next_row ; No, something is wrong...
** Show a page prompt, and wait for response **
add.w #1,dt_YPos(a4) ; Next row
move.w dt_YPos(a4),d0
cmp.w dt_Height(a4),d0 ; Time for a prompt?
bls .next_row ; No
move.w #INITYPOS,dt_YPos(a4) ; Reset YPos
move.l dt_OutFile(a4),d4
move.l d4,d1 ; Output file
SYS Flush ; Make sure prompt is visible (and "enable" reading)
tst.l d0 ; Write ok?
beq.b .prompt_error ; No
move.l d4,d1 ; Output file
move.l #1,d2 ; Select RAW mode
SYS SetMode ; Go to RAW mode
tst.l d0 ; All ok?
beq.b .prompt_error ; No
move.l d4,d1 ; Output file
lea PagePrompt_Msg(pc),a0
move.l a0,d2 ; Page prompt
SYS FPuts ; Show it
tst.l d0 ; All ok
bge.b .prompt_ok ; Yep
.prompt_error:
SYS IoErr ; Get failure reason
move.l d0,d3 ; Save it
move.l d4,d1 ; Output file
move.l #0,d2 ; Go to CON mode
SYS SetMode ; Try to restore normal "CON" mode
move.l d4,d1 ; Output file
lea EndPrompt_Msg(pc),a0
move.l a0,d2 ; Try to erase prompt
SYS FPuts ; Do it
move.l d3,d0 ; Error code
lea PromptError_Msg(pc),a0 ; Error eader
CALL PrintFault
.prompt_ok:
move.l d4,d1 ; Output file
SYS Flush ; Make sure prompt is visible (and "enable" reading)
tst.l d0 ; Write ok?
beq.b .prompt_error ; No
move.l d4,d1 ; Output file
lea dt_Response(a4),a0
move.l a0,d2 ; Read buffer
move.l #1,d3 ; No. of bytes to read
SYS Read ; Wait for a keypress
move.l d0,d6 ; Save read length
bmi.b .prompt_error ; Error!
move.l d4,d1 ; Output file
lea EndPrompt_Msg(pc),a0
move.l a0,d2 ; Remove prompt
SYS FPuts ; Do it
tst.l d0 ; All ok?
bmi .prompt_error ; No
move.l d4,d1 ; Output file
SYS Flush ; Make sure prompt gets removed
tst.l d0 ; All ok?
beq .prompt_error ; No
move.l d4,d1 ; Output file
move.l #0,d2 ; Goto CON mode
SYS SetMode ; Restore normal console mode
tst.l d0 ; All ok?
beq .prompt_error ; No
tst.l d6 ; Read ok?
beq.b .file_break ; EOF => File breaked
tst.l dt_Flags+f_Cursor(a4) ; Hide cursor?
bne.b .no_hide ; No
move.l d4,d1 ; Output file
lea CursorOff_Msg(pc),a0
move.l a0,d2 ; Hide cursor
SYS FPuts ; Do it
tst.l d0 ; All ok?
bmi .prompt_error ; No
.no_hide:
CALL CalcBytesPerRow
move.l d0,d7
move.b dt_Response(a4),d0 ; Get key user pressed
cmp.b #3,d0 ; Ctrl-C?
beq .break ; Yep
cmp.b #4,d0 ; Ctrl-D?
bne.b .0 ; No
.file_break:
CALL PrintFileBreak ; Show break message
bra .done ; And end this file
.0: cmp.b #27,d0 ; Esc?
beq .break ; Yep
cmp.b #'s',d0 ; Skip page prompts for this file
bne.b .1
.clr_height:
clr.w dt_Height(a4) ; Stop paging for this file
bra .cont
.1: cmp.b #'S',d0 ; Skip page prompts for this file
beq.b .clr_height
cmp.b #8,d0 ; Backspace => Previous page
bne.b .4
CLD 1
SYS SetIoErr ; Clear any error code
CLD 0
move.w dt_Height(a4),d0 ; Get height
add.w d0,d0 ; * 2 (really skip 2 pages)
sub.w #3,d0 ; A little overlap + page prompts space
mulu d7,d0 ; Pager only active if this is != 0
sub.l d0,dt_BytePos(a4) ; Go backwards
bge.b .seek_ok ; No seek beyond start
clr.l dt_BytePos(a4) ; Reset pos to start
.seek_ok:
move.w #INITYPOS-1,dt_YPos(a4) ; Reset YPos
move.l dt_InFile(a4),d1 ; Input file
move.l dt_BytePos(a4),d2 ; Offset to seek to
move.l #OFFSET_BEGINNING,d3 ; Offset is relative to start
SYS Seek ; Do the seeking
move.l d0,d3 ; Save result
SYS IoErr ; Any error?
exg.l d0,d3
tst.l d0 ; Check seek result
bmi .read_error ; Error!
tst.l d3 ; IoErr() returned error?
bne .read_error ; Yep
lea FF_Msg(pc),a0
move.l dt_OutFile(a4),d1 ; Output file
move.l a0,d2 ; Code to clear screen
SYS FPuts ; Clear it
tst.l d0 ; All ok?
bmi .write_error ; Nope
move.l dt_OutFile(a4),d1 ; Output file
SYS Flush ; Make sure cursor is restored
tst.l d0 ; All ok?
beq .write_error ; No
bra.b .cont ; And continue with next row etc.
.4: cmp.b #'=',d0 ; Disable page prompt for rest of files
bne.b .2
clr.l dt_Flags+f_Page(a4) ; Disable paging
bra.b .cont
.2: cmp.b #13,d0 ; Return (= new line)?
bne.b .cont
move.w #30000,dt_YPos(a4) ; Only do one line...
.cont: bra .next_row ; And continue with next row
.break:
CALL PrintBreak ; Break pressed
CLD 0 ; No error
CLD 1
CALL CleanExit ; Exit
** All done. Clean up and return **
.done:
tst.l dt_Flags+f_Cursor(a4) ; Cursor hidden?
bne.b .end ; No
move.l dt_OutFile(a4),d1 ; Output file
lea CursorOn_Msg(pc),a0
move.l a0,d2 ; Restore cursor
SYS FPuts ; Do it
tst.l d0 ; All ok?
bmi .write_error ; No
move.l dt_OutFile(a4),d1 ; Output file
SYS Flush ; Make sure cursor is restored
tst.l d0 ; All ok?
beq .write_error ; No
.end:
POPM
rts
*
* LONG CalcBytesPerRow( VOID );
*
* Calculates the number of bytes that will fit on one row.
* Also sets the dt_Height variable properly (if possible).
*
CalcBytesPerRow
PUSHM d1-2
CALL GetWinSize ; Try to get size of console window
CLD 0
CLD 1
move.w dt_Width(a4),d0 ; Get requested width of line
tst.l dt_Flags+f_NoOffset(a4) ; Show offset?
bne.b .offset ; No
sub.w #8,d0 ; Make room for offset
moveq #8,d1
.offset:
cmp.b #ASCII,dt_DisplayFormat(a4) ; Only Ascii?
bne.b .not_ascii ; No
sub.w #2,d0 ; For the single-quotes
bra.b .bytes_done
.not_ascii:
cmp.b #HEX,dt_DisplayFormat(a4) ; Only Hex?
bne.b .both ; No
lsl.w #2,d0 ; * 4
divu #9,d0 ; And make it bytes/row
bra.b .bytes_done
.both: sub.w #2,d0 ; Remove some space (single-quotes)
lsl.w #2,d0 ; * 4
divu #13,d0 ; And make it bytes/row
CLD 2
move.w d0,d2
lsl.w #3,d2 ; * 8
add.w d0,d2 ; And add old value again => * 9
lsr.w #2,d2 ; / 4
add.w d2,d1 ; Add hex part
move.w d0,d2
and.w #3,d2 ; Even multiple of 4?
beq.b .bytes_done ; Yep
addq.w #1,d1
.bytes_done:
move.w d1,dt_EndOfHex(a4) ; Save end pos for hex part
and.l #$0000ffff,d0 ; Clear upper word
POPM
rts
*
* LONG CheckBreak( VOID );
*
* Check if Ctrl-C or Ctrl-D have been pressed. If Ctrl-C, exit "normally".
* If Ctrl-D, print that a file break occured, and return TRUE. Else
* return FALSE.
*
CheckBreak
PUSHM d1/a0-1/a6
move.l dt_SysBase(a4),a6
CLD 0
move.l #SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D,d1 ; Bits we are checking
SYS SetSignal ; Check which signals we have got
btst #SIGBREAKB_CTRL_C,d0 ; Ctrl-C pressed?
beq.b .no_c ; No
CALL PrintBreak
CLD 0
CLD 1
CALL CleanExit
.no_c:
CLD 1 ; Default return is FALSE
btst #SIGBREAKB_CTRL_D,d0 ; Ctrl-D pressed?
beq.b .done ; No
CALL PrintFileBreak
move.l #1,d1
.done:
move.l d1,d0 ; Get return code
POPM
rts
*
* VOID GetWinSize( VOID );
*
* Set the dt_Width and dt_Height fields properly.
* If this routine discovers that dt_Width becomes to small
* (i.e. output window is too narrow), this routine will exit with
* a suitable message.
*
REPORTBUF EQU 16
GetWinSize
PUSHM d0-4/a0-1/a6
tst.l dt_Flags+f_Width(a4) ; Width specified?
beq.b .get ; Nope, get suitable value
tst.l dt_Flags+f_Page(a4) ; Page output?
beq .end ; No, no need to do anything..
.get: move.l dt_OutFile(a4),d4 ; Output file
move.l d4,d1
SYS IsInteractive,dt_DOSBase(a4)
tst.l d0 ; Interactive?
beq .no_page ; No
move.l d4,d1 ; Output file
SYS Flush
tst.l d0 ; All ok?
beq .restore_mode ; No
move.l d4,d1 ; Output file
move.l #1,d2 ; RAW mode
SYS SetMode
tst.l d0 ; Ok?
beq .end ; No
move.l d4,d1 ; Output file
lea GetWinBounds_Msg(pc),a0
move.l a0,d2 ; Ask for window bounds report
move.l #GetWinBounds_Len,d3 ; Length of string
SYS Write ; Write string
tst.l d0 ; All ok
bmi .restore_mode ; No!
move.l d4,d1 ; Output file
move.l #10000,d2 ; Wait .1 secs
SYS WaitForChar ; Wait for reply on request
tst.l d0 ; Anything in return?
beq .restore_mode ; No, no need waiting..
sub.l #REPORTBUF,sp ; Get stack space for report
move.l d4,d1 ; Output file
move.l sp,d2 ; Read buffer
move.l #REPORTBUF,d3 ; Size of buffer
SYS Read ; Get report
move.l d0,d3 ; Number of chars read
move.l d4,d1 ; Output file
move.l #0,d2 ; CON mode
SYS SetMode ; Goto CON mode
cmp.l #9,d3 ; Less than 8 chars read?
ble .bad_report ; Yep, bad report
move.l sp,a0 ; Input buffer
cmp.b #';',4(a0) ; A valid return?
bne.b .bad_report ; No
cmp.b #'r',-1(a0,d3.w) ; More validations..
bne.b .bad_report
add.l #5,a0 ; Get first char of y size
CLD 0
move.b (a0)+,d0 ; Get first char
sub.w #'0',d0 ; Make it "normal" number
cmp.b #';',(a0) ; More Y?
beq.b .x ; No
mulu #10,d0
add.b (a0)+,d0 ; Next char
sub.b #'0',d0 ; Convert to number
cmp.b #';',(a0) ; More Y?
beq.b .y_done ; No
mulu #10,d0 ; We limit y size to 999 lines... :)
add.b (a0)+,d0 ; Get last char
sub.b #'0',d0 ; And convert it
.y_done:
tst.l dt_Flags+f_Page(a4) ; Page output?
beq.b .x ; No
move.w d0,dt_Height(a4) ; Save height
.x: tst.l dt_Flags+f_Width(a4) ; Width specified?
bne.b .bad_report ; Yep, ignore this part
addq.l #1,a0 ; Skip separator
CLD 0
move.b (a0)+,d0 ; Get first char
cmp.b #' ',d0 ; Bad one?
beq.b .report_done ; Yes
cmp.b #';',d0 ; Bad?
beq.b .report_done ; Yes
cmp.b #'r',d0 ; Bad?
beq.b .report_done ; Yes
sub.b #'0',d0 ; Make it "normal"
cmp.b #' ',(a0) ; Any more proper char?
beq.b .report_done ; No
mulu #10,d0
add.b (a0)+,d0 ; Next char
sub.b #'0',d0 ; Make it "normal"
cmp.b #' ',(a0) ; End of report?
beq.b .report_done ; Yep
add.b (a0)+,d0 ; Get last char
sub.b #'0',d0
.report_done:
move.w d0,dt_Width(a4) ; Save width
.bad_report:
add.l #REPORTBUF,sp ; Restore stack
.end: POPM
rts
.restore_mode:
move.l d4,d1 ; Output file
move.l #0,d2 ; RAW mode
SYS SetMode
bra.b .end
.no_page:
clr.l dt_Flags+f_Page(a4) ; Disable page prompts
st dt_Flags+f_Cursor(a4) ; Make Flags.Cursor != NULL
bra.b .end
*
* LONG Printf1( __A0 STRPTR format, __D0 ULONG arg1 );
*
* Printf format using arg1 as the only argument in the argument array.
* The return is the return value from VPrintf.
*
Printf1
PUSHM d1-2/a0-1/a6
move.l dt_DOSBase(a4),a6
PUSH d0 ; Save argument
move.l a0,d1 ; Format
move.l sp,d2 ; Argument array
SYS VPrintf ; Print string
addq.l #4,sp ; Restore stack
POPM
rts
*
* VOID PrintBreak( VOID );
*
* Simply print out a break message. May exit, if write failed.
*
PrintBreak
PUSH a0
lea Break_Msg(pc),a0 ; Format string
CALL __PrintBreakDummy
POP
rts
*
* VOID PrintFileBreak( VOID );
*
* Simply print out a file break message. May exit, if write failed.
*
PrintFileBreak
PUSH a0
lea FileBreak_Msg(pc),a0 ; Format string
CALL __PrintBreakDummy
POP
rts
*
* VOID __PrintBreakDummy( __A0 STRPTR msg );
*
* "Dummy" for printing break messages.
*
__PrintBreakDummy
PUSHM d0/a1/a6
lea CursorOn_Msg(pc),a1 ; Argument
move.l a1,d0
CALL Printf1 ; Print error message
tst.l d0 ; All ok?
bge.b .end ; Yep
move.l dt_DOSBase(a4),a6
SYS IoErr ; Reason for failure
lea PrgName_Msg(pc),a0
CALL PrintFault
.end: POPM
rts
MinWidthArray
MINWIDTHARRAY
*
* Start of string section
*
dc.b "$VER: HexD 2.0 (21.02.93)",0
DOSName_Msg
dc.b "dos.library",0
PrgName_Msg
dc.b "HexD",0
Template_Msg
TEMPLATE
Header_Msg
dc.b "==== File ""%s"" ====",10,0
MAX3 SET MAXWIDTH/100
MAX2 SET (MAXWIDTH-MAX3*100)/10
MAX1 SET MAXWIDTH-MAX2*10-MAX3*100
WidthError_Msg
dc.b "HexD: WIDTH out of range (allowed range: %ld - "
dc.b MAX3+'0',MAX2+'0',MAX1+'0',")",10,0
NoToFile_Msg
dc.b "HexD: Couldn't open TO file ""%s"":",10,0
NoAppend_Msg
dc.b "HexD: Couldn't append to TO file ""%s"":",10,0
NoInFile_Msg
dc.b "HexD: Couldn't open ""%s"" for input:",10,0
WriteError_Msg
dc.b "HexD: Write error",0
ReadError_Msg
dc.b "HexD: Read error",0
PromptError_Msg
dc.b "HexD: Prompt error",0
NarrowWindow_Msg
dc.b "HexD: Output window is too narrow",10,0
NeedOS2_Msg
dc.b "HexD: You need OS 2.04+",10,0
NeedOS2_SIZEOF EQU *-NeedOS2_Msg
PagePrompt_Msg
dc.b 13,$9b,"7m --More-- "
dc.b $9b,$30,$20,$70,$0
EndPrompt_Msg
dc.b $9b,"0m",13,$9b,"K",0
Break_Msg
dc.b "%sHexD: ***Break",10,0
FileBreak_Msg
dc.b "%sHexD: ***File break",10,0
CursorOn_Msg
dc.b $9b,$20,$70,$0
CursorOff_Msg
dc.b $9b,$30,$20,$70,$0
GetWinBounds_Msg
dc.b $9b,"0 q",0
GetWinBounds_Len EQU *-GetWinBounds_Msg
FF_Msg dc.b 12,0
AsciiTable
dc.b "································ !""#$%&'()*+,-./0123456789"
dc.b ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst"
dc.b "uvwxyz{|}~································· ¡¢£¤¥¦§¨©ª«¬®¯"
dc.b "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéê"
dc.b "ëìíîïðñòóôõö÷øùúûüýþÿ",0
HexTable
dc.b "0123456789abcdef",0
END